<template>
    <!-- 搜索 -->
    <div v-if="search" class="flex items-center mb-10">
        <span>快捷搜索：</span>
        <el-select
            v-model="state.keyword"
            class="w-350"
            placement="top"
            filterable
            remote
            reserve-keyword
            placeholder="请输入地点"
            :remote-method="searchLocation"
            :loading="state.loading"
            @change="selectLocation"
        >
            <el-option
                v-for="item in state.searchRes"
                :key="item.id"
                :label="item.title"
                :value="item.id"
            />
        </el-select>
        <slot name="search"></slot>
    </div>

    <!-- 地图 -->
    <slot name="mapTop"></slot>
    <div id="map" :class="mapClass">
        <!-- 工具 -->
        <div v-if="state.multiOptions.editor" class="toolControl">
            <div
                v-for="(item, index) in overlayList"
                :key="item"
                :class="{
                    toolItem: true,
                    [item]: true,
                    active: index === state.activeToolIndex,
                }"
                @click.stop="onClickTool(item, index)"
            ></div>
            <!-- 删除 -->
            <el-popover
                v-if="showDelTool"
                placement="right"
                :width="200"
                trigger="click"
            >
                <template #reference>
                    <el-icon
                        size="25"
                        color="#789cff"
                        class="bg-white p-7.5 m-1 border-rd-3 del-icon"
                        @click.stop="cancelSelectedTool"
                        ><Delete
                    /></el-icon>
                </template>
                <!-- 所有图形 -->
                <div
                    v-for="(group, type) in state.allGeometries"
                    :key="group.label"
                >
                    <el-text
                        v-if="group.options.length"
                        size="small"
                        type="info"
                        >{{ group.label }}</el-text
                    >
                    <div>
                        <el-tag
                            v-for="(item, index) in group.options"
                            :key="item.value"
                            class="mt-5 mb-5 mr-5 cursor-pointer active-tag"
                            closable
                            @mouseenter="highlight(type, item.value)"
                            @mouseleave="unHighlight(type)"
                            @close="delGeometry(type, index, item.value)"
                        >
                            图形{{ index + 1 }}
                        </el-tag>
                    </div>
                </div>
                <!-- 空值提示 -->
                <div v-if="!geometriesCount" class="text-center">
                    <el-text size="small" type="info">暂无数据</el-text>
                </div>
            </el-popover>
        </div>
    </div>
</template>

<script setup>
import {
    onMounted,
    onBeforeUnmount,
    reactive,
    watch,
    nextTick,
    ref,
    inject,
    computed,
    toRaw,
} from "vue";

const emit = defineEmits([
    "clickMap", // 点击地图时触发
    "chooseSearchRes", // 选择搜索结果时触发
    "drawComplete", // 完成图形绘制时触发
    "delGeometry", // 删除图形时触发
    "markerClick", // 激活marker工具点击地图时触发
    "clickTool", // 点击工具时触发
]);
const props = defineProps({
    // 地图样式
    mapClass: {
        type: [String || Object],
        default: "",
    },
    // 是否开启关键词搜索组件
    search: {
        type: Boolean,
        default: true,
    },
    // 点击地图是否移动到点
    clickPanTo: {
        type: Boolean,
        default: true,
    },
    // 地图配置
    mapOptions: {
        type: Object,
        default: () => {},
    },
    // 几何图形和编辑器配置
    multiOptions: {
        type: Object,
        default: () => {},
    },
    // 点标记数据
    markerGeometries: {
        type: Array,
        default: () => [],
    },
    // 多边形数据
    polygonGeometries: {
        type: Array,
        default: () => [],
    },
    // 圆形数据
    circleGeometries: {
        type: Array,
        default: () => [],
    },
    // 点标记样式
    markerStyles: {
        type: Object,
        default: () => {},
    },
});
const $message = inject("$message");
const INTERACT = TMap.tools.constants.EDITOR_ACTION.INTERACT; // 交互模式 可删除修改
const DRAW = TMap.tools.constants.EDITOR_ACTION.DRAW; // 绘制模式 可绘制新图形
const state = reactive({
    keyword: "",
    loading: false,
    searchRes: [],
    multiOptions: {
        // 几何图形和编辑器配置
        marker: true, // 是否开启点标记
        polygon: false, // 是否开启多边形
        circle: false, // 是否开启圆形
        editor: false, // 是否开启编辑器
        overlayList: ["polygon", "circle"], // 可编辑图层
        actionMode: INTERACT, // 编辑器模式
        snappable: true, // 是否开启吸附
    },
    activeToolIndex: null,
    selectedId: null,
    allGeometries: [
        {
            label: "多边形",
            value: "polygon",
            options: [],
        },
        {
            label: "圆形",
            value: "circle",
            options: [],
        },
    ],
});

// 可编辑图层
const overlayList = computed(() => state.multiOptions?.overlayList || []);
// 所有图形数量
const geometriesCount = computed(() =>
    state.allGeometries.reduce((pre, cur) => pre + cur.options.length, 0)
);
// 是否显示删除工具 可编辑图层包含多边形或圆形时显示
const showDelTool = computed(() =>
    overlayList.value.some((item) => ["polygon", "circle"].includes(item))
);

onMounted(() => {});

onBeforeUnmount(() => {
    destroyMap();
});

// 几何图形和编辑器配置变化时，更新几何图形和编辑器
watch(
    () => props.multiOptions,
    (val) => {
        state.multiOptions = { ...state.multiOptions, ...val };

        if (map) {
            const { marker, polygon, circle, editor } = state.multiOptions;
            // 点标记
            marker ? createMultiMarker() : destroyMultiMarker();
            // 多边形
            polygon ? createMultiPolygon() : destroyMultiPolygon();
            // 圆形
            circle ? createMultiCircle() : destroyMultiCircle();
            // 编辑器
            editor ? createGeometryEditor() : destroyGeometryEditor();
        }
    },
    { deep: true }
);

let map = null; // 地图
let marker = null; // 点标记
let polygon = null; // 多边形
let circle = null; // 圆形
let editor = null; // 编辑器
let highlightGeometryStyle = {
    color: "rgba(255,255,0,0.2)",
    borderColor: "rgba(255,255,0,1)",
}; // 高亮图形样式

const initMap = (id) => {
    const { mapOptions, clickPanTo } = props;

    // 初始化地图
    map = new TMap.Map(id || "map", {
        zoom: 10, // 设置地图缩放级别
        center: new TMap.LatLng(25.046399, 102.709165), // 设置地图中心点坐标 云南省人民政府
        ...mapOptions,
    });

    //监听地图点击事件
    map.on("click", (evt) => {
        const latLng = evt.latLng;
        // 触发点击事件
        emit("clickMap", { latLng });
        // 触发marker点击事件
        if (overlayList.value[state.activeToolIndex] === "marker") {
            emit("markerClick", { latLng });
        }

        // 编辑器激活时，点击地图不进行移动
        if (!editor?.getActiveOverlay()) {
            clickPanTo && panTo(latLng);
        }
    });

    createMultiMarker();
};

const destroyMap = () => {
    if (map) {
        map.destroy();
        map = null;
        console.log("销毁地图");
    }
};

// 创建点标记图层
const createMultiMarker = () => {
    if (!marker) {
        marker = new TMap.MultiMarker({
            map: map,
            geometries: props.markerGeometries,
            styles: props.markerStyles,
        });
    }
};

// 销毁点标记图层
const destroyMultiMarker = () => {
    if (marker) {
        marker?.destroy();
        marker = null;
    }
};

// 点标记数据变化 重绘
watch(
    () => props.markerGeometries,
    (val) => {
        setMarker(val);
    },
    { deep: true }
);

// 设置点标记
const setMarker = (geometries) => {
    if (!marker) throw new Error("marker is null");
    marker.setGeometries(toRaw(geometries));
};

// 创建多边形图层
const createMultiPolygon = () => {
    if (!polygon) {
        polygon = new TMap.MultiPolygon({
            map: map,
            geometries: props.polygonGeometries,
            styles: {
                selected: new TMap.PolygonStyle(highlightGeometryStyle),
            },
        });
    }
};

// 销毁多边形图层
const destroyMultiPolygon = () => {
    if (polygon) {
        polygon?.destroy();
        polygon = null;
    }
};

// 多边形数据变化 重绘
watch(
    () => props.polygonGeometries,
    (val) => {
        setPolygon(val);
        // 添加图形到下拉选项中
        state.allGeometries[0].options = val.map((item) => {
            return { value: item.id };
        });
    },
    { deep: true }
);

// 设置多边形
const setPolygon = (geometries) => {
    if (!polygon) throw new Error("polygon is null");
    polygon.setGeometries(toRaw(geometries));
};

// 创建圆形图层
const createMultiCircle = () => {
    if (!circle) {
        circle = new TMap.MultiCircle({
            map: map,
            geometries: props.circleGeometries,
            styles: {
                selected: new TMap.CircleStyle(highlightGeometryStyle),
            },
        });
    }
};

// 销毁圆形图层
const destroyMultiCircle = () => {
    if (circle) {
        circle?.destroy();
        circle = null;
    }
};

// 圆形数据变化 重绘
watch(
    () => props.circleGeometries,
    (val) => {
        setCircle(val);
        // 添加图形到下拉选项中
        state.allGeometries[1].options = val.map((item) => {
            return { value: item.id };
        });
    },
    { deep: true }
);

// 设置圆形
const setCircle = (geometries) => {
    if (!circle) throw new Error("circle is null");
    circle.setGeometries(toRaw(geometries));
};

// 创建编辑器
const createGeometryEditor = () => {
    if (!editor) {
        const { actionMode, snappable } = state.multiOptions;
        editor = new TMap.tools.GeometryEditor({
            map: map,
            overlayList: state.multiOptions.overlayList.reduce((pre, cur) => {
                switch (cur) {
                    case "polygon":
                        pre.push({
                            overlay: polygon,
                            id: "polygon",
                        });
                    case "circle":
                        pre.push({
                            overlay: circle,
                            id: "circle",
                        });
                }
                return pre;
            }, []),
            actionMode, // 编辑器的工作模式
            snappable, // 开启吸附
        });
        // 默认不激活图层
        editor.setActiveOverlay(null);
        editor.on("draw_error", (e) => {
            $message({ message: "仅支持简单多边形", type: "warning" });
        });
        editor.on("draw_complete", (e) => {
            emit("drawComplete", e);
            // 添加图形到删除列表中
            switch (overlayList.value[state.activeToolIndex]) {
                case "polygon":
                    state.allGeometries[0].options.push({
                        value: e.id,
                    });
                    break;
                case "circle":
                    state.allGeometries[1].options.push({
                        value: e.id,
                    });
                    break;
            }
        });
    }
};

// 销毁编辑器
const destroyGeometryEditor = () => {
    if (editor) {
        editor?.destroy();
        editor = null;
    }
};

// 将地图中心平滑移动到指定的经纬度坐标
const panTo = (latLng, duration = 500) => {
    if (!map) throw new Error("map is null");
    map.panTo(latLng, {
        duration,
    });
};

// 搜索地点
const searchLocation = (e) => {
    if (e.trim().length) {
        let search = new TMap.service.Suggestion({ pageSize: 10 });
        state.loading = true;
        search
            .getSuggestions({
                keyword: e,
            })
            .then((res) => {
                console.log(res);
                state.loading = false;
                state.searchRes = res.data;
            });
    }
};

// 选择搜索地点
const selectLocation = (e) => {
    const location = state.searchRes.find((item) => item.id === e);
    if (location) {
        emit("chooseSearchRes", { location });
        props.clickPanTo && panTo(location.location);
    }
};

// 点击工具
const onClickTool = (tool, index) => {
    let isChoose = true;
    emit("clickTool", {
        tool,
        stop() {
            isChoose = false;
        },
    });
    if (isChoose) {
        // 选中/反选工具
        if (index !== state.activeToolIndex) {
            // 选中
            state.activeToolIndex = index;
            // 激活编辑工具
            const activeTool = overlayList.value[state.activeToolIndex];
            // 多边形、圆形 才激活
            if (["polygon", "circle"].includes(activeTool)) {
                editor.setActionMode(DRAW);
                editor.setActiveOverlay(activeTool);
            } else {
                editor.setActiveOverlay(null);
            }
        } else {
            // 取消选中
            cancelSelectedTool();
        }
    }
};

// 取消选中工具
const cancelSelectedTool = () => {
    state.activeToolIndex = null;
    editor?.setActiveOverlay(null);
};

let highlightId = null;
// 高亮图形
const highlight = (type, id) => {
    switch (type) {
        case 0:
            addPolygonClassById(id, "selected");
            break;
        case 1:
            addCircleClassById(id, "selected");
            break;
    }

    highlightId = id;
};

// 取消高亮图形
const unHighlight = (type) => {
    switch (type) {
        case 0:
            addPolygonClassById(highlightId, "default");
            break;
        case 1:
            addCircleClassById(highlightId, "default");
            break;
    }
};

// 删除图形
const delGeometry = (type, index, id) => {
    state.allGeometries[type].options.splice(index, 1);

    switch (type) {
        case 0:
            polygon.remove(id);
            break;
        case 1:
            circle.remove(id);
            break;
    }
    emit("delGeometry", {
        overlay: state.allGeometries[type].value,
        id: id,
    });
};

// 添加多边形样式
const addPolygonClassById = (id, className) => {
    const geometry = polygon.getGeometryById(id);
    geometry.styleId = className;
    polygon.updateGeometries(geometry);
};

// 添加圆形样式
const addCircleClassById = (id, className) => {
    const geometry = circle.getGeometryById(id);
    geometry.styleId = className;
    circle.updateGeometries(geometry);
};

defineExpose({
    initMap,
    panTo,
    destroyMap,
    cancelSelectedTool,
    getMarker: () => marker,
    getPolygon: () => polygon,
    getCircle: () => circle,
});
</script>

<style lang="less" scoped>
#map {
    // 移除水印和控件
    & > :deep(div:last-child) > div:last-child {
        display: none;
    }
}

:deep(.compass-img) {
    width: auto !important;
}

.marker {
    background-image: url("https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/marker_editor.png");
}

.polyline {
    background-image: url("https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/polyline.png");
}

.polygon {
    background-image: url("https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/polygon.png");
}

.circle {
    background-image: url("https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/circle.png");
}

.rectangle {
    background-image: url("https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/rectangle.png");
}

.ellipse {
    background-image: url("https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/ellipse.png");
}

.active-tag:hover {
    border: 1px solid #409eff;
}

.del-icon {
    position: relative;
    &:hover::before {
        position: absolute;
        content: "";
        width: 40px;
        height: 40px;
        border: 1px solid #789cff;
        border-radius: 3px;
    }
}

.toolControl {
    display: flex;
    justify-content: center;
    position: absolute;
    top: 10px;
    left: 0px;
    right: 0px;
    margin: auto;
    z-index: 1001;

    .toolItem {
        width: 40px;
        height: 40px;
        margin: 1px;
        padding: 4px;
        border-radius: 3px;
        background-size: 30px 30px;
        background-position: 4px 4px;
        background-repeat: no-repeat;
        box-shadow: 0 1px 2px 0 #e4e7ef;
        background-color: #ffffff;
        border: 1px solid #ffffff;
        box-sizing: border-box;

        &:hover {
            border-color: #789cff;
        }

        &.active {
            border-color: #d5dff2;
            background-color: #d5dff2;
        }
    }
}
</style>
